Use new this.model(dto).save() for creates, find()/findById() for reads, findByIdAndUpdate() with { new: true } for updates, and findByIdAndDelete() for deletes. Use populate() for reference resolution and aggregate() for complex aggregation pipelines.
Always call .exec() on Mongoose queries to get a true Promise instead of a thenable.
findByIdAndUpdate() with { new: true } returns the updated document — without it returns the original.
populate() resolves ObjectId references to full documents — use it only when the related data is needed.
aggregate() returns plain objects, not Mongoose documents — no virtuals or instance methods.
Use lean() on read-heavy queries to return plain JS objects instead of Mongoose documents for better performance.